<?php

class CCoreBaseDataPointer
{
	const BUFFER_SIZE = 512;

	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @var	resource
	 */
	private $_rData;

	/**
	 * @var	int
	 */
	private $_iOffset;
	
	/**
	 * @var	int
	 */
	private $_iSize;
	#</editor-fold>

	/**
	 * @param	string		$mData
	 * @param	int			$niOffset = null
	 * @param	int			$niSize = null
	 * @return	CCoreBaseDataPointer
	 */
	public function __construct(CCoreBaseDataPointer $oData = null, $niOffset = null, $niSize = null)
	{
		if ($oData)
		{
			$this->_rData = $oData->Data();
			$this->_iOffset = (null === $niOffset) ? $oData->Offset() : $oData->Offset() + $niOffset;
			$this->_iSize = (null === $niSize) ?  $oData->Size() - $niOffset : $niSize;
		}
		else
		{
			$this->_rData = null;
			$this->_iOffset = 0;
			$this->_iSize = 0;
		}
	}

	/**
	 * @param	int		$niOffset = null
	 * @param	int		$niSize = null
	 * @return	CCoreBaseDataPointer
	 */
	public function GetPartAsDataPointer($niOffset = null, $niSize = null)
	{
		return new self($this, $niOffset, $niSize);
	}

	/**
	 * @param	string	$sData
	 * @return	bool
	 */
	public function InitByString($sData)
	{
		$this->_rData = fopen('php://memory', 'r+b');
		if (is_resource($this->_rData))
		{
			$this->_iOffset = 0;
			$this->_iSize = strlen($sData);
			@fwrite($this->_rData, $sData);
			@fseek($this->_rData, $this->_iOffset);
			return true;
		}
		
		return false;
	}

	/**
	 * @param	string	$sData
	 * @return	bool
	 */
	public function InitByFile($sFilePath)
	{
		$this->_rData = @fopen($sFilePath, 'r+b');
		if (is_resource($this->_rData))
		{
			$this->_iOffset = 0;
			$aStat = @fstat($this->_rData);
			$this->_iSize = isset($aStat['size']) ? (int) $aStat['size'] : 0;
			@fseek($this->_rData, $this->_iOffset);
			return true;
		}
		
		return false;
	}

	/**
	 * @return	void
	 */
	public function __destruct()
	{
		/*if (is_resource($this->_rData))
		{
			echo 'close';
			@fclose($this->_rData);
		}*/
	}

	public function GetBufferUntilString($sSearchEnd)
	{
		$iCurrentPosition = $this->GetPos();
			
		$aReturn = array();
		$sBuffer = '';
		while (true)
		{
			$sPrevBuffer = $sBuffer;
			$sBuffer = $this->GetBuffer();
			if (false === $sBuffer)
			{
				if (count($aReturn) == 0)
				{
					return false;
				}
				break;
			}

			$mPos = strpos($sPrevBuffer.$sBuffer, $sSearchEnd);
			if (false !== $mPos)
			{
				$iSeek = $iCurrentPosition + $mPos - strlen($sPrevBuffer) + strlen($sSearchEnd);
				$this->Seek($iSeek);
				$aReturn[] = substr($sBuffer, 0, $iSeek - $iCurrentPosition);
				break;
			}

			$aReturn[] = $sBuffer;
			$iCurrentPosition += strlen($sBuffer);
		}

		return implode($aReturn);
	}

	public function GetBuffer()
	{
		$iCurrentPosition = $this->GetPos();
		if ($iCurrentPosition >= $this->_iSize)
		{
			return false;
		}

		$sBufferSize = self::BUFFER_SIZE;
		if ($sBufferSize >= $iCurrentPosition + $this->_iSize)
		{
			$sBufferSize = $iCurrentPosition + $this->_iSize;
		}

		return fread($this->_rData, $sBufferSize);
	}

	public function Search($mSearch, $niOffset = null)
	{
		$niOffset = (null === $niOffset) ? 0 : $niOffset;
		$this->Seek($niOffset);
		$iCurrentPosition = $niOffset;

		$sBuffer = '';
		while (true)
		{
			$sPrevBuffer = $sBuffer;
			$sBuffer = $this->GetBuffer();
			if (false === $sBuffer)
			{
				break;
			}
			else
			{
				$mSearch = is_string($mSearch) ? array($mSearch) : $mSearch;
				if (is_array($mSearch))
				{
					foreach ($mSearch as $sSearch)
					{
						$mPos = strpos($sPrevBuffer.$sBuffer, $sSearch);
						if (false !== $mPos)
						{
							$iSeek = $iCurrentPosition + $mPos - strlen($sPrevBuffer);
							$this->Seek($iSeek);
							return $iSeek;
						}
					}
				}

				$iCurrentPosition += strlen($sBuffer);
			}
		}

		return false;
	}

	/**
	 * @return	int
	 */
	public function GetPos()
	{
		return ftell($this->_rData) - $this->_iOffset;
	}

	/**
	 * @return	string | bool
	 */
	public function GetString()
	{
		return $this->GetStringFromPosition(0);
	}

	public function __toString()
	{
		return $this->GetString();
	}

	/**
	 * @return	string | bool
	 */
	public function GetNextString($iLen, $niOffset = null)
	{
		$iCurrentPos = $this->GetPos();
		if (null !== $niOffset)
		{
			$iCurrentPos += $niOffset;
			$this->Seek($iCurrentPos);
		}

		$iLen = ($iCurrentPos + $iLen > $this->_iSize) ? $this->_iSize - $iCurrentPos : $iLen;
		return @fread($this->_rData, $iLen);
	}

	/**
	 * @return	string | bool
	 */
	public function GetStringFromPosition($niSeek = null, $niLin = null)
	{
		if (null !== $niSeek)
		{
			$this->Seek($niSeek);
		}
		else
		{
			$niSeek = 0;
		}

		if (null === $niLin)
		{
			$niLin = $this->_iSize - $niSeek;
		}
		else
		{
			$niLin = ($niSeek + $niLin > $this->_iSize) ? $this->_iSize : $niLin;
		}
		
		return @fread($this->_rData, $niLin);
	}

	/**
	 * @param	int		$iOffset
	 * @return	bool
	 */
	public function Seek($iOffset)
	{
		$iRealOffset = $this->_iOffset + $iOffset;
		$iRealOffset = ($iRealOffset > $this->_iOffset + $this->_iSize)
			? $this->_iOffset + $this->_iSize : $iRealOffset;
		
		return (0 === fseek($this->_rData, $iRealOffset));
	}

	/**
	 * @return	resource
	 */
	public function Data()
	{
		return $this->_rData;
	}

	/**
	 * @return	int
	 */
	public function Offset()
	{
		return $this->_iOffset;
	}

	/**
	 * @return	int
	 */
	public function Size()
	{
		return $this->_iSize;
	}
}